﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

namespace XNASpelProjekt
{
    /// <summary>
    /// Class for the player.
    /// Only one instance of this should exist on a map at any time
    /// </summary>
    public class PlayerActor : JumpingActor
    {
        #region Member Variables

        protected int movementSpeed;        //spelarens förflyttningshastighet
        protected int moveKeyPressedTime;   //innehåller tiden som en springknapp (vänster-/högerpil) hållits intryckt
        protected int jumpKeyPressedTime;         //lagrar tiden om hur länge hoppknappen hållits intryckt
        protected bool jumpKeyReleased;
        public int Velocity { get; set; }   //spelarens nuvarande förflyttningshastighet
        protected int reloadDelayTime;      //innehåller tiden som passerat sedan senaste skott
        protected int reloadDelayTimeLimit; //tiden innan spelaren kan skjuta igen  

        protected enum StateType { normal, frozen, damaged, dying }    //spelarens möjliga tillstånd
        protected StateType state;                                     //spelarens nuvarande tillstånd
        protected enum AnimationType { stand, step, jump, damaged }
        protected AnimationType animationType;
        protected AnimationType previousAnimationType;

        protected KeyboardState previousKeyboardState;
        protected KeyboardState currentKeyboardState;

        #endregion

        #region protected methods

        /// <summary>
        /// Checks for collision with enemies
        /// </summary>
        /// <param name="collision">The player's collision rectangle</param>
        protected void EnemyActorCollision(Rectangle collision)
        {
            foreach (EnemyActor e in Game1.map.enemies)
            {
                if (collision.Intersects(e.Bounds))
                {                                        
                    int velocity;
                    if (e.X < X)
                    {
                        velocity = 1;
                    }
                    else
                    {
                        velocity = -1;
                    }
                    Damage(e.CollisionDamage, velocity);
                    state = StateType.damaged;
                    break;                 
                }
            }
        }

        /// <summary>
        /// performs new state initialization and change state
        /// </summary>
        /// <param name="s">The StateType to change to</param>
        protected void ChangeState(StateType s)
        {            
            switch (s)
            {
                case StateType.damaged:
                    animationType = AnimationType.damaged;
                    stateTimeLimit = 400;
                    break;
                case StateType.dying:
                    Hidden = true;
                    Game1.map.animations.Add(new DeathAnimationActor(Game1.deathImage1, X, Y));                    
                    stateTimeLimit = 1500;
                    break;
            }
            stateTime = 0;
            state = s;
        }

        /// <summary>
        /// Handles sprite animations
        /// </summary>
        protected void Animate()
        {
            switch (animationType)
            {
                case AnimationType.stand:
                    texture = Game1.playerImage;
                    break;
                case AnimationType.step:
                    if (moveKeyPressedTime <= 100)
                    {
                        texture = Game1.playerRunningImage1;
                    }
                    else if (moveKeyPressedTime > 100 && moveKeyPressedTime <= 200)
                    {
                        texture = Game1.playerRunningImage2;
                    }
                    else if (moveKeyPressedTime > 200 && moveKeyPressedTime <= 300)
                    {
                        texture = Game1.playerRunningImage3;
                    }
                    else if (moveKeyPressedTime > 300 && moveKeyPressedTime <= 400)
                    {
                        texture = Game1.playerRunningImage2;
                    }
                    else
                    {
                        moveKeyPressedTime = 0;
                    }
                    break;
                case AnimationType.jump:
                    texture = Game1.playerJumpingImage;
                    break;
                case AnimationType.damaged:
                    texture = Game1.playerDamagedImage;
                    break;
            }
        }

        /// <summary>
        /// handles left, right movement
        /// </summary>
        /// <param name="movementSpeed">The speed the movement should be done with. Use negative values for moving left.</param>
        /// <param name="collision">The player's collision rectangle</param>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected void Step(int movementSpeed, Rectangle collision, GameTime gameTime)
        {            
            collision.X += movementSpeed;
            switch (CollisionDetection(collision))
            {
                case _CollisionType.noCollision:
                    X += movementSpeed;
                    animationType = AnimationType.step;
                    moveKeyPressedTime += gameTime.ElapsedGameTime.Milliseconds;
                    break;
                case _CollisionType.death:
                    Health = 0;
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// handles shooting
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected void Shoot(GameTime gameTime)
        {
            if (previousKeyboardState.IsKeyUp(Keys.C))
            {
                if (reloadDelayTime == 0)
                {
                    Game1.playerStandardShotSound.Play();
                    if (Facing == _Facing.left)
                    {
                        Game1.map.bullets.Add(new PlayerStandardShot(X - Game1.playerStandardShotImage.Width, Y + (Bounds.Width / 2) - (Game1.playerStandardShotImage.Height / 2), Math.PI));
                    }
                    else
                    {
                        Game1.map.bullets.Add(new PlayerStandardShot(X + Bounds.Width, Y + (Bounds.Width / 2) - (Game1.playerStandardShotImage.Height / 2), 0));
                    }
                    reloadDelayTime += gameTime.ElapsedGameTime.Milliseconds;
                }
            }
            if (reloadDelayTime > 0)
            {
                reloadDelayTime += gameTime.ElapsedGameTime.Milliseconds;
                if (reloadDelayTime > reloadDelayTimeLimit)
                {
                    reloadDelayTime = 0;
                }
            }
            
        }

        /// <summary>
        /// Handles jumping
        /// </summary>
        /// <param name="collision">The player's collision rectangle</param>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected void Jump(Rectangle collision, GameTime gameTime)
        {
            

            if (jumpKeyPressedTime < jumpTimeLimit)
            {
                jumpKeyPressedTime += gameTime.ElapsedGameTime.Milliseconds;
                jumpKeyReleased = false;

                if (previousKeyboardState.IsKeyDown(Keys.X))
                {
                    //texture = Game1.playerJumpingImage;
                    int jumpspeed = Game1.map.GravityStrength;                    
                    collision = Bounds;
                    collision.Y -= jumpspeed;
                    switch (CollisionDetection(collision))
                    {
                        case _CollisionType.noCollision:
                            Y -= (jumpspeed + Game1.map.GravityStrength + Weight);
                            break;
                        case _CollisionType.death:
                            Health = 0;
                            break;
                        default:
                            break;
                    }
                } 
            }
        }

        /// <summary>
        /// Handles gravity
        /// </summary>
        /// <param name="collision">The player's collision rectangle</param>
        protected override void Gravity(Rectangle collision)
        {
            collision = this.Bounds;
            int fallspeed = Weight + Game1.map.GravityStrength;
            collision.Y += fallspeed;
            switch (CollisionDetection(collision))
            {
                case _CollisionType.noCollision:
                    if (jumpKeyPressedTime == 0)
                    {
                        jumpKeyPressedTime = jumpTimeLimit + 1;
                    }                    
                    Y += fallspeed;
                    animationType = AnimationType.jump;
                    break;
                case _CollisionType.collision:
                    if(jumpKeyReleased == true)
                    {
                        jumpKeyPressedTime = 0;
                    }                    
                    break;
                case _CollisionType.death:
                    Health = 0;
                    break;
                default:
                    break;
            }
        }

        #endregion

        #region public methods

        /// <summary>
        /// Damages the player
        /// </summary>
        /// <param name="damage">The amount of damage inflicted</param>
        /// <param name="velocity">The velocity the player should travel with after getting damaged</param>
        public void Damage(int damage, int velocity)
        {
            if (state == StateType.normal)
            {
                Game1.damageSound.Play();
                Health -= damage;
                jumpKeyPressedTime = jumpTimeLimit + 1;
                Velocity = velocity;
                if (Health > 0)
                {
                    ChangeState(StateType.damaged);
                }
                else
                {
                    ChangeState(StateType.dying);
                }                                  
            }

        }

        /// <summary>
        /// Updates the player in the game.
        /// Keyboard input is read here.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            Rectangle collision;
            currentKeyboardState = Keyboard.GetState();
            collision = Bounds;

            switch(state)
            {
                case StateType.normal:

                    animationType = AnimationType.stand;

                    EnemyActorCollision(collision);
                    if (Health <= 0)
                    {
                        ChangeState(StateType.dying);
                        return;
                    }                    

                    //move left                                           
                    if (currentKeyboardState.IsKeyDown(Keys.Left))
                    {
                        Facing = _Facing.left;
                        Step(-movementSpeed, collision, gameTime);
                    }
                    else if (previousKeyboardState.IsKeyDown(Keys.Left))
                    {
                        moveKeyPressedTime = 0;
                    }

                    //move right
                    if (currentKeyboardState.IsKeyDown(Keys.Right))
                    {
                        Facing = _Facing.right;
                        Step(movementSpeed, collision, gameTime);
                    }
                    else if (previousKeyboardState.IsKeyDown(Keys.Right))
                    {
                        moveKeyPressedTime = 0;
                    }

                    //shoot
                    if (currentKeyboardState.IsKeyDown(Keys.C) && PlayerBulletActor.Count < 5)
                    {                       
                        Shoot(gameTime);                                                                                            
                    }

                    Gravity(collision);

                    //jump
                    if (currentKeyboardState.IsKeyDown(Keys.X))
                    {
                        Jump(collision, gameTime);                  
                    }
                    else if (previousKeyboardState.IsKeyDown(Keys.X) && jumpKeyPressedTime > 0)   //spelaren släpper hoppknappen
                    {
                        jumpKeyPressedTime = jumpTimeLimit + 1;
                        jumpKeyReleased = true;
                    }
                    
                    previousKeyboardState = currentKeyboardState;
                    break;

                case StateType.damaged:      
                    X += Velocity;
                    stateTime += gameTime.ElapsedGameTime.Milliseconds;  
                    if(stateTime >= stateTimeLimit){
                        ChangeState(StateType.normal);
                    }
                    break;
                case StateType.dying:
                    stateTime += gameTime.ElapsedGameTime.Milliseconds;
                    if (stateTime >= stateTimeLimit)
                    {
                        Game1.map = new TitleScreen();
                    }
                    break;
            }
            Animate();                        
        }

        #endregion

        #region constructors
        /// <summary>
        /// Create a player object.
        /// Only one instance of this should exist on a map at any time
        /// </summary>
        /// <param name="texture">The player's texture</param>
        /// <param name="x">(Optional) The player's x-position</param>
        /// <param name="y">(Optional) The player's y-position</param>
        /// <param name="facing">(Optional) The player's facing</param>
        /// <param name="hidden">(Optional) A hidden actor will not be drawn to screen</param>
        public PlayerActor(Texture2D texture, int x = 0, int y = 0, _Facing facing = _Facing.right, bool hidden = false) : base(texture, x, y, facing, hidden)
        {
            movementSpeed = 3;            
            jumpKeyPressedTime = 0;
            jumpTimeLimit = 300;
            jumpKeyReleased = true;
            reloadDelayTime = 0;
            reloadDelayTimeLimit = 80;
            stateTime = 0;
            previousKeyboardState = Keyboard.GetState();
            Health = 15;
            state = StateType.normal;
            animationType = AnimationType.stand;
        }
        #endregion
    }
}
